package com.zytech.zspider.task.impl.http;

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import com.zytech.modules.lang.Langs;
import com.zytech.modules.lang.Strings;
import com.zytech.zspider.core.TaskDefinition;
import com.zytech.zspider.task.TaskContext;
import org.apache.commons.codec.binary.Base64;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.DomSerializer;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.w3c.dom.Document;

import java.util.Map;

/**
 * @author siuming
 */
public class HttpClientTaskExecutor extends AbstractHttpTaskExecutor {

    /**
     * @param scriptEvaluator
     */
    public HttpClientTaskExecutor() {
        this(DEFAULT_BLOCKING_FACTOR);
    }

    /**
     * @param blockingFactor
     */
    public HttpClientTaskExecutor(float blockingFactor) {
        super(
                blockingFactor,
                new String[]{
                        "/spider-task.groovy",
                        "/spider-task-httpclient.groovy"
                },
                new HttpClientTaskRequestExecutor()
        );
    }

    @Override
    protected boolean isHttpTaskSupports(TaskDefinition task, Map<String, String> settings) {
        String executor = settings.get(TaskDefinition.Settings.HTTP_EXECUTOR);
        return Strings.isBlank(executor) || Objects.equal(executor, "executor.default");
    }

    @Override
    protected TaskContext ofCollectTaskResultContext(TaskDefinition task, TaskContext context, HttpTaskRequest request, HttpTaskResponse response) {

        if (response.isTextMimeType()) {
            Document doc = parseDocument(response);
            context.putBinding("_$doc", doc);
        }

        if (response.isImageMimeType() || response.isOctetMimeType()) {
            context.putBinding("_$content_type", response.getMimeType());
            context.putBinding("_$content", Base64.encodeBase64String(response.getContent()));
        }

        context.putAllBindings(ImmutableMap.of("_$req", request, "_$resp", response));
        return context;
    }

    private Document parseDocument(HttpTaskResponse response) {
        try {

            HtmlCleaner cleaner = ofCleaner(response);
            TagNode node = cleaner.clean(response.getContentAsString());
            return ofSerializer(cleaner.getProperties()).createDOM(node);
        } catch (Exception e) {
            throw Langs.toUncheck(e);

        }
    }

    private HtmlCleaner ofCleaner(HttpTaskResponse response) {
        HtmlCleaner cleaner = new HtmlCleaner();
        cleaner.getProperties().setAdvancedXmlEscape(true);
        cleaner.getProperties().setAllowMultiWordAttributes(true);
        cleaner.getProperties().setOmitComments(true);
        cleaner.getProperties().setOmitUnknownTags(false);
        cleaner.getProperties().setRecognizeUnicodeChars(true);
        cleaner.getProperties().setTranslateSpecialEntities(true);
        cleaner.getProperties().setUseCdataForScriptAndStyle(true);
        cleaner.getProperties().setOmitCdataOutsideScriptAndStyle(true);
        cleaner.getProperties().setCharset(response.getCharset());
        return cleaner;
    }

    private DomSerializer ofSerializer(CleanerProperties props) {
        return new DomSerializer(props);
    }
}
